//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

import React, { useCallback, useEffect, useRef, useState } from 'react';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { DndProvider, DropTargetMonitor, useDrop } from 'react-dnd';
import { observer } from 'mobx-react';
import { useRoadmapStores } from './store';
import ReactFlow, { Background, Panel, ReactFlowProvider, Node as FlowNode, Edge as FlowEdge, OnNodesChange, NodePositionChange, NodeDimensionChange, OnConnect, MarkerType, MiniMap, ConnectionMode } from 'reactflow';
import type { EdgeInfo, EdgeKey, NodeInfo } from '@/api/roadmap_content';
import { NODE_GROUP, update_node_position, update_node_size, add_edge, EDGE_BEZIER, NODE_TOPIC } from '@/api/roadmap_content';
import { useSize } from 'ahooks';
import { Button, Segmented, Space } from 'antd';
import NodeListPanel from './components/NodeListPanel';
import { DND_ITEM_TYPE } from '@/api/roadmap';
import type { DragNodeInfo } from './components/nodes/DragNode';
import { request } from '@/utils/request';
import { allEdgeTypes, allNodeTypes, getNodeTypeStr } from './components/nodes/helper';
import DataEditDrawer from './components/DataEditDrawer';
import BatchEditNodePanel from './components/BatchEditNodePanel';
import RoadmapInfoPanel from './components/RoadmapInfoPanel';
import { ExpandOutlined, LockOutlined, MinusOutlined, PlusOutlined, UnlockOutlined } from '@ant-design/icons';
import { createNode } from './components/nodes/utils';


const RoadmapEditor = observer(() => {
    const roadmapStore = useRoadmapStores();

    const divRef = useRef<HTMLDivElement>(null);
    const divSize = useSize(divRef);

    const [nodes, setNodes] = useState<FlowNode<NodeInfo>[]>([]);
    const [edges, setEdges] = useState<FlowEdge<EdgeInfo>[]>([]);

    const createEdge = async (fromNodeId: string, fromHandleId: string, toNodeId: string, toHandleId: string) => {
        if (roadmapStore.inEdit == false) {
            return;
        }

        const fromNode = roadmapStore.getNode(fromNodeId);
        const toNode = roadmapStore.getNode(toNodeId);
        if (fromNode == undefined || toNode == undefined) {
            return;
        }

        const edgeKey: EdgeKey = {
            from_node_id: fromNodeId,
            from_handle_id: fromHandleId,
            to_node_id: toNodeId,
            to_handle_id: toHandleId,
            roadmap_id: roadmapStore.roadmapId,
        };

        let edgeColor = "black";
        let edgeWidth = 2;

        if (fromNode.basic_info.node_type == NODE_TOPIC && toNode.basic_info.node_type == NODE_TOPIC) {
            edgeColor = "blue";
            edgeWidth = 4;
        }


        await request(add_edge({
            session_id: roadmapStore.sessionId,
            edge_key: edgeKey,
            basic_info: {
                edge_type: EDGE_BEZIER,
                edge_color: edgeColor,
                has_begin_arrow: false,
                has_end_arrow: fromNode.basic_info.node_type == NODE_TOPIC && toNode.basic_info.node_type == NODE_TOPIC,
                edge_width: edgeWidth,
            }
        }));
        roadmapStore.onUpdateEdge(edgeKey);
    };


    const [_, drop] = useDrop(() => ({
        accept: DND_ITEM_TYPE,
        drop: (itemData: DragNodeInfo, monitor: DropTargetMonitor<DragNodeInfo, void>) => {
            if (roadmapStore.flowInstance != null) {
                const clientOffset = monitor.getClientOffset();
                const position = roadmapStore.flowInstance.screenToFlowPosition({
                    x: (clientOffset?.x ?? 0),
                    y: (clientOffset?.y ?? 0),
                });
                createNode(roadmapStore, itemData, position);
            }
        },
        canDrop: () => true,
    }));

    const updateNodePosition = async (nodeId: string) => {
        if (roadmapStore.inEdit == false) {
            return;
        }
        const node = roadmapStore.getNode(nodeId);
        if (node == undefined) {
            return;
        }
        await request(update_node_position({
            session_id: roadmapStore.sessionId,
            roadmap_id: roadmapStore.roadmapId,
            node_id: nodeId,
            node_position: node.basic_info.node_position,
        }));
    };

    const updateNodeSize = async (nodeId: string) => {
        if (roadmapStore.inEdit == false) {
            return;
        }
        const node = roadmapStore.getNode(nodeId);
        if (node == undefined) {
            return;
        }
        await request(update_node_size({
            session_id: roadmapStore.sessionId,
            roadmap_id: roadmapStore.roadmapId,
            node_id: nodeId,
            node_size: node.basic_info.node_size,
        }));
    }

    const onNodesChange: OnNodesChange = useCallback(
        (changes) => {
            if (roadmapStore.inEdit == false) {
                return;
            }
            if (roadmapStore.lockSizeAndPosition) {
                return;
            }
            for (const change of changes) {
                if (change.type == "position" && roadmapStore.batchNodeIdList.includes(change.id) == false) {
                    const realChange = change as NodePositionChange;
                    if (realChange.dragging) {
                        roadmapStore.onUpdateNodePosition(change.id, realChange.position?.x ?? 0, realChange.position?.y ?? 0);
                    } else {
                        updateNodePosition(change.id);
                    }
                } else if (change.type == "dimensions" && roadmapStore.batchNodeIdList.includes(change.id) == false) {
                    const realChange = change as NodeDimensionChange;
                    if (realChange.resizing) {
                        let newWidth = realChange.dimensions?.width ?? 0;
                        if (newWidth < 50) {
                            newWidth = 50;
                        }
                        let newHeight = realChange.dimensions?.height ?? 0;
                        if (newHeight < 30) {
                            newHeight = 30;
                        }
                        roadmapStore.onUpdateNodeSize(change.id, newWidth, newHeight);
                    } else {
                        updateNodeSize(change.id);
                    }
                }
            }
        },
        [setNodes]
    );

    const onConnect: OnConnect = useCallback(
        (conn) => {
            if (roadmapStore.inEdit == false) {
                return;
            }
            if (conn.source == conn.target || conn.source == null || conn.target == null || conn.sourceHandle == null || conn.targetHandle == null) {
                return;
            }
            createEdge(conn.source, conn.sourceHandle, conn.target, conn.targetHandle);
        },
        [setEdges]
    );

    useEffect(() => {
        const tmpNodeList: FlowNode<NodeInfo>[] = [];
        for (const node of roadmapStore.nodeList) {
            tmpNodeList.push({
                id: node.node_id,
                data: node,
                position: node.basic_info.node_position,
                width: node.basic_info.node_size.w,
                height: node.basic_info.node_size.h,
                type: getNodeTypeStr(node.basic_info.node_type),
                zIndex: node.basic_info.node_type == NODE_GROUP ? 10 : 100,
            });
        }
        setNodes(tmpNodeList);

        const tmpEdgeList: FlowEdge<EdgeInfo>[] = [];
        for (const edge of roadmapStore.edgeList) {
            tmpEdgeList.push({
                id: `${edge.edge_key.from_node_id}:${edge.edge_key.from_handle_id}:${edge.edge_key.to_node_id}:${edge.edge_key.to_handle_id}`,
                source: edge.edge_key.from_node_id,
                target: edge.edge_key.to_node_id,
                sourceHandle: edge.edge_key.from_handle_id,
                targetHandle: edge.edge_key.to_handle_id,
                data: edge,
                type: "RoadmapEdge",
                zIndex: 1000,
                markerStart: edge.basic_info.has_begin_arrow ? {
                    type: MarkerType.Arrow,
                    color: edge.basic_info.edge_color,
                    strokeWidth: 2,
                } : undefined,
                markerEnd: edge.basic_info.has_end_arrow ? {
                    type: MarkerType.Arrow,
                    color: edge.basic_info.edge_color,
                    strokeWidth: 2,
                } : undefined,
            });
        }
        setEdges(tmpEdgeList);
    }, [roadmapStore.nodeList, roadmapStore.edgeList]);


    const fitView = () => {
        if (divSize == undefined || roadmapStore.flowInstance == null) {
            return;
        }
        let mostTopNode: FlowNode<NodeInfo> | null = null;
        for (const node of nodes) {
            if (mostTopNode == null) {
                mostTopNode = node;
            } else {
                if (node.data.basic_info.node_position.y < mostTopNode.data.basic_info.node_position.y) {
                    mostTopNode = node;
                }
            }
        }
        if (mostTopNode == null) {
            return;
        }
        let mostLeftNode: FlowNode<NodeInfo> | null = null;
        let mostRightNode: FlowNode<NodeInfo> | null = null;
        //找到高度相差500以内的点的最左和最有节点
        for (const node of nodes) {
            if (node.data.basic_info.node_position.y - 500 > mostTopNode.data.basic_info.node_position.y) {
                continue;
            }
            if (mostLeftNode == null) {
                mostLeftNode = node;
            } else {
                if (node.data.basic_info.node_position.x < mostLeftNode.data.basic_info.node_position.x) {
                    mostLeftNode = node;
                }
            }
            if (mostRightNode == null) {
                mostRightNode = node;
            } else {
                if ((node.data.basic_info.node_position.x + node.data.basic_info.node_size.w) > (mostRightNode.data.basic_info.node_position.x + mostRightNode.data.basic_info.node_size.w)) {
                    mostRightNode = node;
                }
            }
        }
        if (mostLeftNode == null || mostRightNode == null) {
            roadmapStore.flowInstance?.fitView();
        } else {
            roadmapStore.flowInstance?.fitView({
                padding: 0.2,
                nodes: [{ id: mostTopNode.id }, { id: mostLeftNode.id }, { id: mostRightNode.id }],
            });
        }
    };

    useEffect(() => {
        fitView();
    }, [divSize?.height, divSize?.width, roadmapStore.flowInstance]);

    useEffect(() => {
        if (roadmapStore.inEdit == false) {
            fitView();
        }
    }, [nodes, roadmapStore.inEdit]);

    return (
        <ReactFlowProvider>
            <div ref={divRef} style={{ width: "100vw", height: "100vh" }}>
                <ReactFlow ref={drop} onInit={(instance) => roadmapStore.flowInstance = instance}
                    deleteKeyCode={null} onNodesChange={onNodesChange} onConnect={onConnect} connectionMode={ConnectionMode.Loose}
                    nodes={nodes} nodeTypes={allNodeTypes} panOnScroll
                    edges={edges} edgeTypes={allEdgeTypes}
                    nodeDragThreshold={5}
                    onMouseMove={e => {
                        if (roadmapStore.inEdit) {
                            roadmapStore.mousePosition = {
                                x: e.clientX,
                                y: e.clientY,
                            };
                        }
                    }}
                >
                    <Background color="#777" style={{ backgroundColor: "#eee" }} />
                    <Panel position="bottom-left">
                        <Space style={{ backgroundColor: "white" }}>
                            <Button type="text" icon={<MinusOutlined />} onClick={e => {
                                e.stopPropagation();
                                e.preventDefault();
                                roadmapStore.flowInstance?.zoomOut();
                            }} title='缩小' />
                            <Button type="text" icon={<PlusOutlined />} onClick={e => {
                                e.stopPropagation();
                                e.preventDefault();
                                roadmapStore.flowInstance?.zoomIn();
                            }} title='放大' />
                            <Button type="text" icon={<ExpandOutlined />} onClick={e => {
                                e.stopPropagation();
                                e.preventDefault();
                                fitView();
                            }} title='适配视图' />
                            {roadmapStore.inEdit && (
                                <Button type="text" icon={roadmapStore.lockSizeAndPosition ? <LockOutlined /> : <UnlockOutlined />}
                                    onClick={e => {
                                        e.stopPropagation();
                                        e.preventDefault();
                                        roadmapStore.lockSizeAndPosition = !roadmapStore.lockSizeAndPosition;
                                    }} title={roadmapStore.lockSizeAndPosition ? "锁定位置和大小" : "未锁定位置和大小"}
                                    disabled={roadmapStore.batchNodeIdList.length > 0} />
                            )}
                        </Space>
                    </Panel>
                    {roadmapStore.canUpdate && roadmapStore.inEdit && (
                        <Panel position="top-left">
                            <NodeListPanel />
                        </Panel>
                    )}
                    {roadmapStore.inEdit == false && roadmapStore.adminUser == false && (
                        <Panel position="top-left">
                            <RoadmapInfoPanel />
                        </Panel>
                    )}
                    {roadmapStore.canUpdate && roadmapStore.batchNodeIdList.length == 0 && (
                        <Panel position="top-right">
                            <Segmented options={[
                                {
                                    label: "查看状态",
                                    value: "read",
                                    disabled: roadmapStore.adminUser,
                                },
                                {
                                    label: "编辑状态",
                                    value: "edit",
                                }
                            ]} value={roadmapStore.inEdit ? "edit" : "read"}
                                onChange={value => {
                                    if (value == "read") {
                                        roadmapStore.inEdit = false;
                                    } else if (value == "edit") {
                                        roadmapStore.inEdit = true;
                                    }
                                }} />
                        </Panel>
                    )}
                    {roadmapStore.canUpdate && roadmapStore.batchNodeIdList.length > 0 && (
                        <Panel position="top-right">
                            <BatchEditNodePanel />
                        </Panel>
                    )}
                    <MiniMap nodeStrokeWidth={5} nodeColor="yellow" nodeStrokeColor="black" maskColor='rgba(200,200,200,0.8)' pannable zoomable zoomStep={1} />
                </ReactFlow>
            </div>
            {(roadmapStore.selectNodeId != "" || roadmapStore.selectEdgeKey != null) && (
                <DataEditDrawer />
            )}
        </ReactFlowProvider>
    );
});

const RoadmapBoard = () => {
    return (
        <DndProvider backend={HTML5Backend}>
            <RoadmapEditor />
        </DndProvider>
    );
};

export default RoadmapBoard;